﻿using System;
using System.Collections.Generic;

namespace Gqq.Csharpindepth.Common
{
    public class GqqBase
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    public class GqqDerive : GqqBase
    {
        public string Address { get; set; }
    }

    public class VarianceAndContravariance
    {
        public static void Test()
        {
            IEnumerable<GqqDerive> derives = new List<GqqDerive>
                                               {
                                                   new GqqDerive {Id = 1, Name = "name1", Address = "ss1"},
                                                   new GqqDerive {Id = 2, Name = "gqq2", Address = "ss2"},
                                                   new GqqDerive {Id = 3, Name = "gqq3", Address = "ss2"}
                                               };
            //Covariance(协变)——和多态挺像。
            IEnumerable<GqqBase> bases = derives;

            foreach (var gqqBase in bases)
            {
                //其实，gqqBase本质上的属性就是GqqDerive。这个可以通过反射来实现出来
                //以下输出 Gqq.Csharpindepth.Common.GqqDerive
                Console.WriteLine(gqqBase.GetType().FullName);
                Console.WriteLine("{0}_{1}", gqqBase.Id, gqqBase.Name);
                GqqLog.WriteLog("{0}_{1}", gqqBase.Id, gqqBase.Name);
            }

            //然而，“逆变”主要用于委托
//            Action<GqqBase> ab = delegate(GqqBase b) { Console.WriteLine(b.GetType().Name); };
            Action<GqqBase> ab = b => Console.WriteLine(b.GetType().Name);
            Action<GqqDerive> db = ab;// 感觉似乎降级了。
            ab(new GqqBase());
            ab(new GqqDerive());
            db(new GqqDerive());
            //注意，这个是不可以的。因为泛型db已经规定了必须传GqqDerive类型的参数。
//            db(new GqqBase());


            //一个典型的逆变的例子。
            //委托OutputHandler2需要传入GqqDerive类型的参数，但是，如果有传入GqqBase类型参数的方法，
            //照样可以绑定在这个委托上，这就是逆变。
//            GqqBase gb = new GqqBase {Id = 1, Name = "li"};
            GqqDerive gd = new GqqDerive {Id = 2, Name = "zhang", Address = "furongli"};

            //逆变不支持匿名委托，要特别注意。
//            OutputDerivedHandler oh2 = delegate(GqqBase b) { return b.Id + " " + b.Name; };
//            OutputHandler1 oh1 = oh2;

            //对于OutputHandler2来说，绑定的方法传入的参数应该是GqqDerive类型的。但是
            //它也支持绑定传入GqqDerive父类型参数的方法。所以，参数类型支持逆变，就是这个意思。
            OutputDerivedHandler odh = Output;
            odh(gd);
        }

        public static void Output(GqqBase gb)
        {
            Console.WriteLine("Id:{0}\tName:{1}", gb.Id, gb.Name);
        }

    }
}
